/////////////////////////////////////////////////////////////////////////
//
// Amuse Engine SDK - core/containers
// Copyright (c) 2014.  All Rights Reserved
//
// File:		AETQueue.inl
// Author:	Gianluca Belardelli
// Date:		01/06/2014
//
/////////////////////////////////////////////////////////////////////////
#ifndef _AETQUEUE_INL_
#define _AETQUEUE_INL_

template <typename T>
AETQueue<T>::AETQueue( void )
	:	m_lpData( 0L ),
		m_nCapacity( 0 ),
		m_nHead(0),
		m_nTail(0),
		m_nElementsInUse(0)
{
}

template <typename T>
AETQueue<T>::AETQueue( AEINT32 nCapacity  )
	:	m_lpData( AEMemHeapBufAlloc<T>( nCapacity ) ),
		m_nCapacity( nCapacity ),
		m_nHead(0),
		m_nTail(0),
		m_nElementsInUse(0)
{
	AEASSERT_MSG( nCapacity > 0, "this constructor is not appropriate for zero initial capacity" );
}

template <typename T>
AETQueue<T>::~AETQueue( void )
{
	_releaseMemory();
}

template <typename T>
AE_FORCEINLINE AEBOOL32 AETQueue<T>::IsEmpty( void ) const
{
	return m_nElementsInUse == 0;
}

template <typename T>
AE_FORCEINLINE AEINT32 AETQueue<T>::GetSize( void ) const
{
	return m_nElementsInUse;
}

template <typename T>
AE_FORCEINLINE AEINT32 AETQueue<T>::GetCapacity( void ) const
{
	return m_nCapacity;
}

template <typename T>
AE_FORCEINLINE const T *AETQueue<T>::GetData( void ) const
{
	return m_lpData;
}

template <typename T>
AE_FORCEINLINE const T &AETQueue<T>::GetElement( AEINT32 nIdx ) const
{
	AEASSERT( nIdx >= 0 && nIdx < GetSize() );
	return m_lpData[ ( m_nHead + nIdx ) % m_nCapacity ];
}

template <typename T>
AE_FORCEINLINE void AETQueue<T>::Clear( void )
{
	m_nHead = 0;
	m_nTail = 0;
	m_nElementsInUse = 0;
}


template <typename T>
AE_FORCEINLINE void AETQueue<T>::_releaseMemory( void )
{
	if( m_nCapacity )
		//hkDeallocateChunk<T>( m_lpData, m_nCapacity, HK_MEMORY_CLASS_ARRAY );
		AEMemHeapBufFree( m_lpData, m_nCapacity );
}


template <typename T>
AE_FORCEINLINE void AETQueue<T>::SetCapacity( AEINT32 nCapacity )
{
	if( m_nCapacity < nCapacity )
	{
		// allocate a new buffer and copy existing tData over
		if ( m_nCapacity * 2 >= nCapacity )
		{
			nCapacity = m_nCapacity * 2;
		}

		//T* lpPtr = hkAllocateChunk<T>( n , HK_MEMORY_CLASS_ARRAY );
		T *lpPtr = AEMemHeapBufAlloc<T>( nCapacity );

		if( lpPtr != 0L && m_lpData != 0L )
		{
			if( m_nElementsInUse )
			{
				if( m_nTail <= m_nHead )
				{
					// split
					AEINT32 numHead = m_nCapacity - m_nHead;
					AEINT32 numTail = m_nTail;
					// TODO
					//hkString::memCpy( lpPtr, m_lpData+m_nHead, ( sizeof(T) * numHead ) );
					//hkString::memCpy( lpPtr + numHead, m_lpData, ( sizeof(T) * numTail ) );
				}
				else
				{
					// one block
					// TODO
					//hkString::memCpy( lpPtr, m_lpData+m_nHead, ( sizeof(T) * m_nElementsInUse ) );
				}
			}
			m_nHead = 0;
			m_nTail = m_nElementsInUse;
		}

		// release the old buffer
		_releaseMemory();

		// assign to new buffer and set capacity
		m_lpData = (T*)lpPtr;
		m_nCapacity = nCapacity;
	}
}

template <typename T>
AE_FORCEINLINE void AETQueue<T>::_increaseCapacity( void )
{
	if( m_nCapacity == 0 )
	{
		// if there is no current capacity default to 8 elements
		SetCapacity( 8 );
	}
	else
	{
		SetCapacity( m_nCapacity * 2 );
	}
}


// Places a new element to the back of the queue and expand storage if necessary.
template <typename T>
AE_FORCEINLINE void AETQueue<T>::Enqueue( const T &tElem )
{
    if( m_nElementsInUse >= m_nCapacity )
	    _increaseCapacity();

	if( m_nTail == m_nCapacity )
		m_nTail = 0;
	
	m_lpData[m_nTail++] = tElem;
	m_nElementsInUse++;
}

// Places a new element to the front of the queue and expand storage if necessary.
template <typename T>
AE_FORCEINLINE void AETQueue<T>::EnqueueInFront( const T &tElem )
{
    if( m_nElementsInUse >= m_nCapacity )
	    _increaseCapacity();

    if( m_nHead == 0 )
	    m_nHead = m_nCapacity;

    m_lpData[--m_nHead] = tElem;
    m_nElementsInUse++;
}

template <typename T>
AE_FORCEINLINE void AETQueue<T>::Dequeue( T &tData )
{
    AEASSERT( m_nElementsInUse );
    {
	    tData = m_lpData[m_nHead];
	    if( ++m_nHead == m_nCapacity )
		    m_nHead = 0;

	    m_nElementsInUse--;
    }
}

template <typename T>
AE_FORCEINLINE void AETQueue<T>::Peek( T &tData ) const
{
	tData = m_lpData[m_nHead];
}

template <typename T>
AE_FORCEINLINE AEINT32 AETQueue<T>::GetHead( void ) const
{
	return m_nHead;
}

template <typename T>
AE_FORCEINLINE AEINT32 AETQueue<T>::GetTail( void ) const
{
	return m_nTail;
}

#endif // _AETQUEUE_INL_
